package com.insight.service;

import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * T093: MinIO对象存储服务
 * 提供MinIO文件上传、下载、预签名URL等功能
 *
 * @author Insight Team
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MinIOService {

    private final MinioClient minioClient;

    @Value("${minio.bucket.exports}")
    private String exportsBucket;

    @Value("${minio.bucket.archives}")
    private String archivesBucket;

    @Value("${minio.presigned-url-expiry-seconds:604800}")
    private int presignedUrlExpirySeconds;

    private static final int MAX_EXPIRY_SECONDS = 604800; // 7天

    /**
     * 检查bucket是否存在
     *
     * @param bucketName bucket名称
     * @return true如果存在
     * @throws IOException MinIO操作异常
     */
    public boolean bucketExists(String bucketName) throws IOException {
        validateBucketName(bucketName);

        try {
            return minioClient.bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(bucketName)
                            .build()
            );
        } catch (Exception e) {
            log.error("检查bucket失败: bucket={}", bucketName, e);
            throw new IOException("检查bucket失败: " + e.getMessage(), e);
        }
    }

    /**
     * 创建bucket
     *
     * @param bucketName bucket名称
     * @throws IOException MinIO操作异常
     */
    public void createBucket(String bucketName) throws IOException {
        validateBucketName(bucketName);

        try {
            if (!bucketExists(bucketName)) {
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(bucketName)
                                .build()
                );
                log.info("Bucket创建成功: {}", bucketName);
            }
        } catch (Exception e) {
            log.warn("创建bucket失败(可能已存在): bucket={}", bucketName);
            // 忽略bucket已存在的错误
        }
    }

    /**
     * 删除bucket
     *
     * @param bucketName bucket名称
     * @throws IOException MinIO操作异常
     */
    public void deleteBucket(String bucketName) throws IOException {
        validateBucketName(bucketName);

        try {
            minioClient.removeBucket(
                    RemoveBucketArgs.builder()
                            .bucket(bucketName)
                            .build()
            );
            log.info("Bucket删除成功: {}", bucketName);
        } catch (Exception e) {
            log.error("删除bucket失败: bucket={}", bucketName, e);
            throw new IOException("删除bucket失败: " + e.getMessage(), e);
        }
    }

    /**
     * 上传文件(Path参数)
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @param filePath   文件路径
     * @return 对象路径
     * @throws IOException 文件或MinIO操作异常
     */
    public String uploadFile(String bucketName, String objectName, Path filePath) throws IOException {
        return uploadFile(bucketName, objectName, filePath, null);
    }

    /**
     * 上传文件(Path参数，指定ContentType)
     *
     * @param bucketName  bucket名称
     * @param objectName  对象名称
     * @param filePath    文件路径
     * @param contentType 内容类型
     * @return 对象路径
     * @throws IOException 文件或MinIO操作异常
     */
    public String uploadFile(String bucketName, String objectName, Path filePath, String contentType)
            throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);
        validatePath(filePath);

        if (!Files.exists(filePath)) {
            throw new IOException("文件不存在: " + filePath);
        }

        // 自动创建bucket
        ensureBucketExists(bucketName);

        try (InputStream inputStream = Files.newInputStream(filePath)) {
            long fileSize = Files.size(filePath);
            String type = contentType != null ? contentType : Files.probeContentType(filePath);

            return uploadFile(bucketName, objectName, inputStream, fileSize, type);
        }
    }

    /**
     * 上传文件(InputStream参数)
     *
     * @param bucketName  bucket名称
     * @param objectName  对象名称
     * @param inputStream 输入流
     * @param size        文件大小
     * @return 对象路径
     * @throws IOException MinIO操作异常
     */
    public String uploadFile(String bucketName, String objectName, InputStream inputStream, long size)
            throws IOException {
        return uploadFile(bucketName, objectName, inputStream, size, "application/octet-stream");
    }

    /**
     * 上传文件(InputStream参数，指定ContentType)
     *
     * @param bucketName  bucket名称
     * @param objectName  对象名称
     * @param inputStream 输入流
     * @param size        文件大小
     * @param contentType 内容类型
     * @return 对象路径
     * @throws IOException MinIO操作异常
     */
    public String uploadFile(String bucketName, String objectName, InputStream inputStream,
                             long size, String contentType) throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);
        validateInputStream(inputStream);

        // 自动创建bucket
        ensureBucketExists(bucketName);

        try {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, size, -1)
                            .contentType(contentType != null ? contentType : "application/octet-stream")
                            .build()
            );

            log.info("文件上传成功: bucket={}, object={}, size={}", bucketName, objectName, size);
            return objectName;

        } catch (Exception e) {
            log.error("文件上传失败: bucket={}, object={}", bucketName, objectName, e);
            throw new IOException("文件上传失败: " + e.getMessage(), e);
        }
    }

    /**
     * 下载文件到指定路径
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @param targetPath 目标路径
     * @return 下载后的文件路径
     * @throws IOException 文件或MinIO操作异常
     */
    public Path downloadFile(String bucketName, String objectName, Path targetPath) throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);
        validatePath(targetPath);

        // 自动创建父目录
        if (targetPath.getParent() != null && !Files.exists(targetPath.getParent())) {
            Files.createDirectories(targetPath.getParent());
        }

        try (InputStream inputStream = downloadFile(bucketName, objectName)) {
            Files.copy(inputStream, targetPath);
            log.info("文件下载成功: bucket={}, object={}, target={}", bucketName, objectName, targetPath);
            return targetPath;

        } catch (Exception e) {
            log.error("文件下载失败: bucket={}, object={}", bucketName, objectName, e);
            throw new IOException("文件下载失败: " + e.getMessage(), e);
        }
    }

    /**
     * 下载文件为InputStream
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @return 文件输入流
     * @throws IOException MinIO操作异常
     */
    public InputStream downloadFile(String bucketName, String objectName) throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);

        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            log.error("获取文件流失败: bucket={}, object={}", bucketName, objectName, e);
            throw new IOException("获取文件流失败: " + e.getMessage(), e);
        }
    }

    /**
     * 生成下载预签名URL(默认过期时间)
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @return 预签名URL
     * @throws IOException MinIO操作异常
     */
    public String generatePresignedDownloadUrl(String bucketName, String objectName) throws IOException {
        return generatePresignedDownloadUrl(bucketName, objectName, presignedUrlExpirySeconds);
    }

    /**
     * 生成下载预签名URL(自定义过期时间)
     *
     * @param bucketName    bucket名称
     * @param objectName    对象名称
     * @param expirySeconds 过期时间(秒)
     * @return 预签名URL
     * @throws IOException MinIO操作异常
     */
    public String generatePresignedDownloadUrl(String bucketName, String objectName, int expirySeconds)
            throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);
        validateExpirySeconds(expirySeconds);

        try {
            String url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(expirySeconds)
                            .build()
            );

            log.info("生成下载URL成功: bucket={}, object={}, expiry={}s", bucketName, objectName, expirySeconds);
            return url;

        } catch (Exception e) {
            log.error("生成预签名URL失败: bucket={}, object={}", bucketName, objectName, e);
            throw new IOException("生成预签名URL失败: " + e.getMessage(), e);
        }
    }

    /**
     * 生成上传预签名URL
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @return 预签名URL
     * @throws IOException MinIO操作异常
     */
    public String generatePresignedUploadUrl(String bucketName, String objectName) throws IOException {
        return generatePresignedUploadUrl(bucketName, objectName, presignedUrlExpirySeconds);
    }

    /**
     * 生成上传预签名URL(自定义过期时间)
     *
     * @param bucketName    bucket名称
     * @param objectName    对象名称
     * @param expirySeconds 过期时间(秒)
     * @return 预签名URL
     * @throws IOException MinIO操作异常
     */
    public String generatePresignedUploadUrl(String bucketName, String objectName, int expirySeconds)
            throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);
        validateExpirySeconds(expirySeconds);

        try {
            String url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.PUT)
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(expirySeconds)
                            .build()
            );

            log.info("生成上传URL成功: bucket={}, object={}, expiry={}s", bucketName, objectName, expirySeconds);
            return url;

        } catch (Exception e) {
            log.error("生成上传URL失败: bucket={}, object={}", bucketName, objectName, e);
            throw new IOException("生成上传URL失败: " + e.getMessage(), e);
        }
    }

    /**
     * 检查对象是否存在
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @return true如果存在
     * @throws IOException MinIO操作异常
     */
    public boolean objectExists(String bucketName, String objectName) throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);

        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
            return true;
        } catch (ErrorResponseException e) {
            return false;
        } catch (Exception e) {
            log.error("检查对象失败: bucket={}, object={}", bucketName, objectName, e);
            throw new IOException("检查对象失败: " + e.getMessage(), e);
        }
    }

    /**
     * 删除对象
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @throws IOException MinIO操作异常
     */
    public void deleteObject(String bucketName, String objectName) throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);

        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
            log.info("对象删除成功: bucket={}, object={}", bucketName, objectName);
        } catch (Exception e) {
            log.warn("删除对象失败(可能不存在): bucket={}, object={}", bucketName, objectName);
            // 对象不存在时不抛出异常
        }
    }

    /**
     * 获取对象元数据
     *
     * @param bucketName bucket名称
     * @param objectName 对象名称
     * @return 对象元数据
     * @throws IOException MinIO操作异常
     */
    public StatObjectResponse getObjectMetadata(String bucketName, String objectName) throws IOException {
        validateBucketName(bucketName);
        validateObjectName(objectName);

        try {
            return minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            log.error("获取对象元数据失败: bucket={}, object={}", bucketName, objectName, e);
            throw new IOException("获取对象元数据失败: " + e.getMessage(), e);
        }
    }

    /**
     * 复制对象
     *
     * @param sourceBucket 源bucket
     * @param sourceObject 源对象
     * @param destBucket   目标bucket
     * @param destObject   目标对象
     * @throws IOException MinIO操作异常
     */
    public void copyObject(String sourceBucket, String sourceObject, String destBucket, String destObject)
            throws IOException {
        validateBucketName(sourceBucket);
        validateObjectName(sourceObject);
        validateBucketName(destBucket);
        validateObjectName(destObject);

        try {
            minioClient.copyObject(
                    CopyObjectArgs.builder()
                            .bucket(destBucket)
                            .object(destObject)
                            .source(
                                    CopySource.builder()
                                            .bucket(sourceBucket)
                                            .object(sourceObject)
                                            .build()
                            )
                            .build()
            );
            log.info("对象复制成功: {}/{} -> {}/{}", sourceBucket, sourceObject, destBucket, destObject);
        } catch (Exception e) {
            log.error("复制对象失败: {}/{} -> {}/{}", sourceBucket, sourceObject, destBucket, destObject, e);
            throw new IOException("复制对象失败: " + e.getMessage(), e);
        }
    }

    /**
     * 列举bucket中的所有对象
     *
     * @param bucketName bucket名称
     * @return 对象迭代器
     */
    public Iterable<Result<Item>> listObjects(String bucketName) {
        return listObjects(bucketName, null, false);
    }

    /**
     * 列举指定前缀的对象
     *
     * @param bucketName bucket名称
     * @param prefix     对象前缀
     * @return 对象迭代器
     */
    public Iterable<Result<Item>> listObjects(String bucketName, String prefix) {
        return listObjects(bucketName, prefix, false);
    }

    /**
     * 列举对象(递归模式)
     *
     * @param bucketName bucket名称
     * @param prefix     对象前缀
     * @param recursive  是否递归
     * @return 对象迭代器
     */
    public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) {
        validateBucketName(bucketName);

        ListObjectsArgs.Builder builder = ListObjectsArgs.builder()
                .bucket(bucketName)
                .recursive(recursive);

        if (prefix != null && !prefix.isEmpty()) {
            builder.prefix(prefix);
        }

        return minioClient.listObjects(builder.build());
    }

    // ==================== 私有辅助方法 ====================

    /**
     * 确保bucket存在,不存在则创建
     */
    private void ensureBucketExists(String bucketName) throws IOException {
        if (!bucketExists(bucketName)) {
            createBucket(bucketName);
        }
    }

    /**
     * 验证bucket名称
     */
    private void validateBucketName(String bucketName) {
        if (bucketName == null || bucketName.trim().isEmpty()) {
            throw new IllegalArgumentException("Bucket名称不能为空");
        }
    }

    /**
     * 验证对象名称
     */
    private void validateObjectName(String objectName) {
        if (objectName == null || objectName.trim().isEmpty()) {
            throw new IllegalArgumentException("对象名称不能为空");
        }
    }

    /**
     * 验证Path参数
     */
    private void validatePath(Path path) {
        if (path == null) {
            throw new IllegalArgumentException("文件路径不能为null");
        }
    }

    /**
     * 验证InputStream参数
     */
    private void validateInputStream(InputStream inputStream) {
        if (inputStream == null) {
            throw new IllegalArgumentException("输入流不能为null");
        }
    }

    /**
     * 验证过期时间
     */
    private void validateExpirySeconds(int expirySeconds) {
        if (expirySeconds < 0) {
            throw new IllegalArgumentException("过期时间不能为负数");
        }
        if (expirySeconds > MAX_EXPIRY_SECONDS) {
            throw new IllegalArgumentException(
                    String.format("过期时间不能超过%d秒(7天)", MAX_EXPIRY_SECONDS)
            );
        }
    }
}
